﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;

namespace XmlDSigEx
{
    class TransformChain
    {
        private List<ITransform> _transforms = new List<ITransform>();
        private List<XmlElement> _transformElements;

        private ICanonicalisationAlgorithm _canonicalisation = new XmlC14NCanonicalisation();

        internal TransformChain(XmlElement referenceElement, params ITransform[] transforms)
        {
            _transformElements = referenceElement.GetElementsByTagName("Transform", XmlSigner.NS_XMLDSIG).ConvertToListOf<XmlElement>();
            _transforms.AddRange(transforms);
        }

        public Stream ProcessTransforms(object input)
        {
            object data = input;
            foreach (ITransform transform in _transforms)
            {
                XmlElement transformElement = _transformElements[_transforms.IndexOf(transform)];
                if (transform.SupportsInputType(data.GetType()))
                {
                    data = transform.Process(data, transformElement);
                }
                else
                {
                    data = transform.Process(Convert(data), transformElement);
                }
            }
            Stream output = data as Stream;
            if (output != null)
                return output;
            else
            {
                return Convert(data) as Stream;
            }
        }

        private object Convert(object data)
        {
            Stream octetStream = data as Stream;
            if (octetStream != null)
            {
                var reader = XmlReader.Create(octetStream);
                XmlDocument doc = new XmlDocument() { PreserveWhitespace = true };
                doc.Load(reader);    
                return doc.SelectNodes("(//. | //@* | //namespace::*)[not(self::comment())]").ConvertToListOf<XmlNode>();
            }
            List<XmlNode> nodeSet = data as List<XmlNode>;
            if(nodeSet != null)
            {
                return _canonicalisation.ProcessNodeSet(nodeSet, false);
            }
            throw new ArgumentException("A data type that was not either a List<XmlNode> or Stream object was encountered while processing the transform chain.");
        }
    }
}
